#include "myshell.h"
#include <stdlib.h>
#include <ctype.h>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
//void Debug()
//{
//    printf("hello shell!\n");
//}

char* gargv[ARGS];//命令行参数表
int gargc = 0;
char pwd[1024];
int lastcode = 0;

//重定向类型
#define NONE_REDIR 0
#define OUPUT_REDIR 1
#define APPEND_REDIR 2
#define INPUT_REDIR 3

std::string filename;
int redir_type = NONE_REDIR;

void InitGlobal()
{
    //将参数表中历史命令删除
    gargc = 0;
    memset(gargv, 0, sizeof(gargv));
}

//下面三个接口不需要被外部访问就用static修饰
//获取用户名
static std::string GetUsrName()
{
    std::string username = getenv("USER");
    return username.empty() ? "None" : username;
}
//获取主机名
static std::string GetHostName()
{
    std::string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None" : hostname;
}
//获取工作路径
static std::string GetPwd()
{
    //std::string pwd = getenv("PWD"); 
    //return pwd.empty() ? "None" : pwd;
    
    char temp[1024];
    getcwd(temp, sizeof(temp));//实时更新pwd
    //更新PATH中shell自己的pwd
    snprintf(pwd, sizeof(pwd), "PWD=%s", temp);
    putenv(pwd);

    //化简命令提示符中路径形式
    std::string pwd_lable = temp;
    const std::string pwd_sign = "/";
    auto pos = pwd_lable.rfind(pwd_sign);
    if(pos == std::string::npos)
        return "None";
    pwd_lable = pwd_lable.substr(pos + pwd_sign.size());

    return pwd_lable.empty() ? "/" : pwd_lable;
}

//获取家目录
static std::string GetHomePath()
{
    std::string homepath = getenv("HOME");
    return homepath.empty() ? "None" : homepath;
}

void PrintfCommandPrompt()
{
    //方法一:函数调用
    //用户名:
    std::string user = GetUsrName();
    //主机名:
    std::string hostname = GetHostName();
    //工作路径:
    std::string pwd = GetPwd();
    
    //打印命令行
    printf("[%s@%s %s]#", user.c_str(), hostname.c_str(), pwd.c_str());
}

//获取用户键盘输入
bool GetCommandString(char cmd_str_buff[], int len)
{
    if(cmd_str_buff == NULL || len <= 0)
    {
        return false;
    }
    char* res = fgets(cmd_str_buff, len, stdin);
    if(res == NULL)
    {
        return false;
    }
    //去除回车键影响
    cmd_str_buff[strlen(cmd_str_buff) - 1] = 0;//最后一个位置为\n，不需要考虑进去
    return strlen(cmd_str_buff) == 0 ? false : true;
}


// "ls -a -l > log.txt" -> "ls -a -l\0log.txt"; 
// filename = log.txt, redir_type = OUTPUT_REDIR;

//越过空格
#define TrimSpace(start) do{\
    while(isspace(*start))\
   {\
        start++;\
   }\
  }while(0)

void CheckRedir(char cmd[])
{
    char* start = cmd;
    char* end = cmd + strlen(cmd) - 1;
    while(start <= end)
    {
        if(*start == '>')
        {
            if(*(start + 1) == '>')
            {
                //追加重定向
                redir_type = APPEND_REDIR;
                *start = '\0';
                start += 2;
                TrimSpace(start);
                filename = start;
                break;
            }
            else
            {
                //输出重定向
                redir_type = OUPUT_REDIR;
                *start = '\0';
                start++;
                TrimSpace(start);//移除空格
                filename = start;//这时start后面的字符串就是文件名了
                break;
            }
        }
        else if(*start == '<')
        {
            //输入重定向
            redir_type = INPUT_REDIR;
            *start = '\0';
            start++;
            TrimSpace(start);
            filename = start;
            break;
            
        }
        else
        {
            start++;
        }
    }
}

//形成命令行参数表
bool ParseCommandString(char cmd[])
{
    if(cmd == nullptr)
        return false;
#define SIGN " "
    gargv[gargc++] = strtok(cmd, SIGN);
    while((bool)(gargv[gargc++] = strtok(NULL, SIGN)));
    //回退一次:去除最后一个NULL
    gargc--;

//#define DEBUG
#ifdef DEBUG
    printf("gargc: %d\n", gargc);
    printf("------------------------\n");
    for(int i = 0; i < gargc; i++)
    {
        printf("gargv[%d]: %s\n", i, gargv[i]);
    }
    printf("------------------------\n");
    for(int i = 0; gargv[i]; i++)
    { 
        printf("gargv[%d]: %s\n", i, gargv[i]);
    }

#endif

    return true;
}

void ForkAndExec()
{
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");//报错
        return;
    }
    else if(id == 0)
    {
        //子进程
        //进程程序替代
        if(redir_type == OUPUT_REDIR)
        {
            int output = open(filename.c_str(),O_CREAT | O_WRONLY | O_TRUNC , 0666);
            (void)output;
            dup2(output, 1);
        }
        else if(redir_type == APPEND_REDIR)
        {
            int appendfd = open(filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
            (void)appendfd;
            dup2(appendfd, 1);
        }
        else if(redir_type == INPUT_REDIR)
        {
            int input = open(filename.c_str(), O_RDONLY);
            (void)input;
            dup2(input, 0);
        }
        
        execvp(gargv[0], gargv); 
        exit(0);
    }
    else
    {
        //父进程进行进程等待
        int status = 0;
        pid_t rid = waitpid(id, &status, 0);
        if(rid > 0)
        {
            //等待成功
            lastcode = WEXITSTATUS(status); 
        }
    }
}

//内建命令执行
bool BuiltInCommandExec()
{
    std::string cmd = gargv[0];
    bool ret = false;
    if(cmd == "cd")
    {
        if(gargc == 2)
        {
            std::string tar = gargv[1];
            if(tar == "~")
            {
                chdir(GetHomePath().c_str());
                ret = true;
            }
            else
            {
                chdir(gargv[1]);
                ret = true;
            }
        }
        else if(gargc == 1)
        {
            chdir(GetHomePath().c_str());
            ret = true;
        }
    }
    else if(cmd == "echo")
    {
        if(gargc == 2)
        {
            std::string tar = gargv[1];
            if(tar[0] == '$')
            {
                if(tar[1] == '?')
                {
                    printf("lastcode: %d\n", lastcode);
                    lastcode = 0;
                    ret = true;
                }
                else
                {
                    const char* name = &tar[1];
                    printf("%s\n", getenv(name));
                    lastcode = 0;
                    ret = true;
                }
            }

            else
            {
                printf("%s\n", tar.c_str());
                ret = true;
            }
        }
    }
    return ret;
}






